package com.github.wxpay.sdk.api;

import com.github.wxpay.sdk.api.entity.OrderEntity;
import com.github.wxpay.sdk.api.entity.CreateOrderResponseEntity;
import com.github.wxpay.sdk.api.entity.PayStatusEntity;
import com.github.wxpay.sdk.config.WXPayConfig;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.constant.WXPayConstants;
import com.github.wxpay.sdk.exception.WXPayException;
import com.github.wxpay.sdk.util.CollectionUtils;
import com.github.wxpay.sdk.util.WXPayUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.security.provider.MD5;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by tyf on 2018/11/17
 */
public class WxPayApi {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private static final String SUCCESS = "SUCCESS";

    private WXPayConfig config = null;

    public WxPayApi(WXPayConfig config) {
        this.config = config;
    }

    /**
     * 创建订单
     *
     * @throws Exception
     */
    public CreateOrderResponseEntity createOrder(OrderEntity orderEntity) throws Exception {
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("body", orderEntity.getBody());
        data.put("out_trade_no", orderEntity.getOutTradeNo());
        data.put("device_info", orderEntity.getDeviceInfo());
        data.put("fee_type", orderEntity.getFeeType());
        data.put("total_fee", orderEntity.getTotalFee());
        data.put("spbill_create_ip", orderEntity.getSpbillCreateIp());
        data.put("notify_url", orderEntity.getNotifyUrl());
        data.put("trade_type", orderEntity.getTradeType());  // 此处指定为扫码支付
        data.put("product_id", orderEntity.getProductId());
        data.put("openid", orderEntity.getOpenid());

        data = CollectionUtils.mapRemoveWithNullByRecursion(data);

        Map<String, String> resp = wxpay.unifiedOrder(data);
        String returnCode = resp.get("return_code");
        String returnMsg = resp.get("return_msg");
        if (StringUtils.equals(SUCCESS, returnCode)) {
            CreateOrderResponseEntity createOrderResponseEntity = new CreateOrderResponseEntity();
            createOrderResponseEntity.setAppId(resp.get("appid"));

            long currentTimeMillis = System.currentTimeMillis();//生成时间戳
            long second = currentTimeMillis / 1000L;//（转换成秒）
            String seconds = String.valueOf(second).substring(0, 10);//（截取前10位）


            createOrderResponseEntity.setTimeStamp(seconds);
            createOrderResponseEntity.setNonceStr(resp.get("nonce_str"));

            createOrderResponseEntity.setPayPackage(String.format("prepay_id=%s", resp.get("prepay_id")));
            createOrderResponseEntity.setSignType(WXPayConstants.HMACSHA256);
            Map<String, String> describe = BeanUtils.describe(createOrderResponseEntity);
            describe.put("package", describe.get("payPackage"));

            describe.remove("payPackage");
            describe.remove("paySign");
            describe.remove("class");

            createOrderResponseEntity.setPaySign(
                    WXPayUtil.generateSignature(describe, this.config.getKey(), WXPayConstants.SignType.HMACSHA256));
            return createOrderResponseEntity;
        }
        //如果不为success,则直接抛出异常
        log.error(resp.toString());
        throw new WXPayException(returnCode, returnMsg);
    }


    /**
     * 查询订单
     *
     * @throws Exception
     */
    public PayStatusEntity selectOrder(String outTradeNo) throws Exception {

        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", outTradeNo);

        Map<String, String> resp = wxpay.orderQuery(data);
        PayStatusEntity payStatusEntity = fillPayStatusEntity(resp);


        return payStatusEntity;
        //如果不为success,则直接抛出异常
    }


    public static void main(String[] args) throws Exception {
        String s = "<xml><appid><![CDATA[wxf1d959b99f33b156]]></appid><bank_type><![CDATA[CFT]]></bank_type><cash_fee><![CDATA[25000]]></cash_fee><device_info><![CDATA[WEB]]></device_info><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[1347714901]]></mch_id><nonce_str><![CDATA[EveQvThWwrwX85gPalNnVgkJPGEOPRgD]]></nonce_str><openid><![CDATA[oC_UHwmxHpL11HHoDdLHYj3zOm9k]]></openid><out_trade_no><![CDATA[154304442758360]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[CA14D5E3D50E118C7F0066DB78B05B6CB5835806487C5A6D258268782F79B8AF]]></sign><time_end><![CDATA[20181124152712]]></time_end><total_fee>25000</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[4200000209201811240739816076]]></transaction_id></xml>";
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(s);  // 转换成map
        String sign = notifyMap.get(WXPayConstants.FIELD_SIGN);
        System.out.println(WXPayUtil.generateSignature(notifyMap,
                "054f894783fb33511639f3ae7b008bf4", WXPayConstants.SignType.HMACSHA256).equals(sign));

    }


    /**
     * 支付通知
     *
     * @param notifyData
     * @throws Exception
     */
    public PayStatusEntity payNotify(String notifyData) throws Exception {

        log.info(notifyData);

        WXPay wxpay = new WXPay(config);

        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);  // 转换成map

        //TODO 改为true
        if (wxpay.isPayResultNotifySignatureValid(notifyMap) == false) {
            // 签名正确
            // 进行处理。
            // 注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户侧订单状态从退款改成支付成功
            PayStatusEntity payStatusEntity = fillPayStatusEntity(notifyMap);

            return payStatusEntity;
        } else {
            // 签名错误，如果数据里没有sign字段，也认为是签名错误
            log.error("解析支付通知失败:", notifyMap.toString());
            return null;
        }
    }


    /**
     * 查询退款
     *
     * @throws Exception
     */
    public void refundMoney(String outTradeNo) throws Exception {
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", outTradeNo);

        Map<String, String> resp = wxpay.orderQuery(data);
        System.out.println(resp);
    }


    /**
     * 创建通知返回结果
     *
     * @param isSuccess
     * @return
     * @throws Exception
     */
    public String createPayNotifyResultMap(boolean isSuccess) throws Exception {
        Map<String, String> resultMap = new HashMap<>();
        if (isSuccess) {
            resultMap.put("return_code", "SUCCESS");
            resultMap.put("return_msg", "OK");
        } else {
            resultMap.put("return_code", "FAIL");
            resultMap.put("return_msg", "FAIL");
        }
        return WXPayUtil.mapToXml(resultMap);
    }


    /**
     * map转化PayStatusEntity
     *
     * @param resp
     * @return
     */
    private PayStatusEntity fillPayStatusEntity(Map<String, String> resp) {
        //初始化支付状态为未支付
        PayStatusEntity payStatusEntity = new PayStatusEntity();
        payStatusEntity.setOutTradeNo(resp.get("out_trade_no"));
        String settlementTotalFee = resp.get("settlement_total_fee");
        if (StringUtils.isNotBlank(settlementTotalFee)) {
            payStatusEntity.setSettlementTotalFee(Integer.parseInt(settlementTotalFee));
        }
        String totalFee = resp.get("total_fee");
        if (StringUtils.isNotBlank(totalFee)) {
            payStatusEntity.setTotalFee(Integer.parseInt(totalFee));
        }
        payStatusEntity.setPayStatus(payStatusHandle(resp));
        return payStatusEntity;
    }

    /**
     * 解析trade_state
     *
     * @param resp
     * @return
     */
    private Integer payStatusHandle(Map<String, String> resp) {
        int status = 0;
        String tradeState = resp.get("trade_state");
        if (tradeState == null) return 1;
        if (StringUtils.equals(SUCCESS, resp.get("return_code"))) {
            switch (tradeState) {
                case "SUCCESS"://支付成功
                    status = 1;
                    break;
                case "REFUND"://转入退款
                    status = 0;
                    break;
                case "NOTPAY"://未支付
                    status = 0;
                    break;
                case "CLOSED"://已关闭
                    status = 5;
                    break;
                default:
                    status = 6;//交易失败
                    break;
            }
        } else {
            log.error(resp.get("err_code"), resp.get("err_code_des"));//记录失败原因
        }
        return status;
    }
}
